#! /bin/sh
# Archer3协议脚本

# 启动命令
START_CMD="./bin/elderly-ai-care-agent"

# Processor Name
PROCESSOR_NAME="elderly-ai-care-agent"

# PAAS环境
ENV_UNKNOWN=0
ENV_MATRIX=1
ENV_GALAXY=2

# 是否打印日志到标准输出
VERBOSELY=1
# -15 杀死进程时，等待的时间(秒)
# 如果想直接 -9 杀，将该值设为小于0
SIGTERM_LIMIT=-1
# -9 杀死进程时，等待的时间(秒)
SIGKILL_LIMIT=300

# 获取当前脚本的路径，保存到FILE
if [[ -L "$0" ]];then
    FILE=$(readlink -f "$0")
else
    FILE=$0
fi

# BASE_DIR为当前脚本的上级目录，即与bin平级的目录，以此目录查找其它所需文件
BASE_DIR=$(cd $(dirname ${FILE}); pwd)/..

# 脚本启动的服务标准输出日志
CURRENT_TIME=$(date "+%Y%m%d%H%M%S")
mkdir -p "${BASE_DIR}/log/${PROCESSOR_NAME}"
LOG_SERV_STDOUT="${BASE_DIR}/log/${PROCESSOR_NAME}/${PROCESSOR_NAME}.stdout.log.${CURRENT_TIME}"
LOG_SERV_STDERR="${BASE_DIR}/log/${PROCESSOR_NAME}/${PROCESSOR_NAME}.stderr.log.${CURRENT_TIME}"
# 脚本运行日志
LOG_FILE="${BASE_DIR}/log/noah_control.log"
> ${LOG_FILE}

# 日志打印函数
function logging() {
    local timestamp=$(date)
    if [[ ${VERBOSELY} -eq 1 ]];then
        # output to the standard error
        echo "[${timestamp}]$@" >&2
    fi
    echo "[${timestamp}]$@" >> ${LOG_FILE}
}

# warning 日志
function warning() {
    logging "WARNING: $@"
}

 # info 日志
function info() {
    logging "INFO: $@"
}

info "Get base dir: ${BASE_DIR}"

# supervise 所需的目录和文件
SUPERVISE="${BASE_DIR}/supervise/bin/supervise"
SUPERVISE_CONF="${BASE_DIR}/supervise/conf/supervise.conf"
STATUS_DIR="${BASE_DIR}/supervise/status/${PROCESSOR_NAME}"

STATUS_FILE="${STATUS_DIR}/status"
LOCK_FILE="${STATUS_DIR}/lock"
CONTORL_FILE="${STATUS_DIR}/control"

#####################################function###########################
# 获取父进程的进程id，有supervise的情况下父进程就是supervise进程
function get_ppid() {
    #local ret
    # in matrix, this script uses fuser, otherwise, it should use /sbin/fuser
    # ret=$(fuser ${LOCK_FILE} 2>&1 | tail -1 | awk '{print $NF}')
    # if [[ $? -ne 0 ]];then
    #     wraning "Run fuser ${LOCK_FILE} 2>&1 | tail -1 | awk '{print \$NF}' failed!"
    #     exit 1
    # fi
    # echo ${ret}
    
    #local pid=$(get_pid)
    #local ppid=`ps awx -o "%p %P" | grep -w ${pid} | awk '{print $2}' | grep -v ${pid}`

    local ppid=`ps aux | grep supervise | grep "${START_CMD}" | grep -v grep | awk '{print $2}'`
    echo ${ppid}
}

# 获取执行进程的进程id，即supervise监控的进程
function get_pid() {
    #local ret
    #ret=$(od -An -j16 -N2 -tu2 ${STATUS_FILE} | awk '{print $NF}')
    #if [[ $? -ne 0 ]];then
    #    warning "Run od -An -j16 -N2 -tu2i ${STATUS_FILE} | awk '{print \$NF}' failed!"
    #    exit 1
    #fi
    
    local pid=`ps aux | grep "${START_CMD}" | grep -v supervise | grep -v grep | awk '{print $2}'`
    echo ${pid}
}

check_port_alive(){
    port=$1
    /usr/sbin/ss -nl 2>/dev/null | awk '{print $3}' | grep -w ":${port}" &>/dev/null
    if [ $? -ne 0 ];then
        echo "Port ${port} not open!"
        return 1
    else
        echo "Port ${port} is open!"
        return 0
    fi
}


# 执行进程是否处于运行状态
# 返回 0 进程表示运行中，返回 1 表示运行结束
function is_running() {
    pid=$(get_pid)

    echo ${pid}
    if [[ -z "${pid}" ]];then
        warning "does not find ${START_CMD}, process is not running..."
        return 1
    fi

    check_port_alive $PORT_MAIN
    if [ $? -ne 0 ];then
        return 1
    else
        return 0
    fi

    if [[ -d /proc/${pid} ]];then
        warning "/proc/${pid} exist, process is running..."
        return 0
    else
        warning "/proc/${pid} doesn't exist, process is not running..."
        return 1
    fi
}

# 启动
function start() {
    PORT_MAIN_CONF="${BASE_DIR}/conf/web.toml"
    sed -i "s/http_port.*$/http_port = ${PORT_MAIN}/g" $PORT_MAIN_CONF
    is_running
    if [[ $? -eq 0 ]];then
        warning "Process is already running"
        return 0
    fi

    RUN_CMD="nohup ${START_CMD}"
    info "RUN_CMD: ${RUN_CMD}"

    cd ${BASE_DIR}
    # ${SUPERVISE} -f "${RUN_CMD}" -p "${STATUS_DIR}" -F "${SUPERVISE_CONF}" &> /dev/null </dev/null &
    ${SUPERVISE} -f "${RUN_CMD}" -p "${STATUS_DIR}" -F "${SUPERVISE_CONF}" >$LOG_SERV_STDOUT 2>$LOG_SERV_STDERR &

    cd -

    sleep 10

    is_running
    if [[ $? -eq 1 ]];then
        warning "Start failed!..."
        return 1
    fi

    local pid=$(get_pid)
    if [[ -d /proc/${pid} ]];then
        info "Start succ: pid=${pid}"
        return 0
    else
        warning "Start failed"
        return 1
    fi
}

# 停止
function stop() {
    is_running
    if [[ $? -eq 1 ]];then
       warning "Process is not running yet!"
       return 0
    fi

    local supervise_pid=$(get_ppid)
    if [[ -n "${supervise_pid}" ]];then
        info "start killing supervise: kill -n 9 ${supervise_pid}..."
        kill -n 9 ${supervise_pid}
        local ret_val=$?
        while ([[ ${ret_val} -ne 0 ]])
        do
            supervise_pid=$(get_ppid)
            if [[ -z "${supervise_pid}" ]];then
                break
            fi
            info "continue killing supervise: kill -n 9 ${supervise_pid}..."
            kill -n 9 ${supervise_pid}
            ret_val=$?
        done
        
        sleep 1
        while ([[ -n "${supervise_pid}" ]])
        do
            supervise_pid=$(get_ppid)
            info "waiting for supervise to exit..."
            sleep 1
        done

        info "killing supervise success"
    else
        info "does not find supervise, supervise is not running"
    fi
   
    local target_pid=$(get_pid)
    if [[ -z "${target_pid}" ]];then
        info "Stop success: does not find ${START_CMD}, process is not running"
        return 0 
    fi

    info "start exit gracefully: kill -n 15 ${target_pid}..."
    kill -n 15 ${target_pid}
    
    waited=0
    is_running
    while ([[ $? -eq 0 ]] && [[ ${waited} -le ${SIGTERM_LIMIT} ]])
    do
        sleep 5
        waited=$(expr ${waited} + 5)
        info "continue exit gracefully:kill -n 15 ${target_pid}, has waited for ${waited} seconds..."
        kill -n 15 ${target_pid}
        is_running
    done
   
    is_running
    if ([[ $? -eq 0 ]] && [[ ${waited} -gt ${SIGTERM_LIMIT} ]]);then
        warning "waited for ${waited} seconds, time out, going to kill the process..."
        
        info "start killing process: kill -n 9 ${target_pid}..."
        kill -n 9 ${target_pid}
       
        waited=0
        is_running
        while ([[ $? -eq 0 ]] && [[ ${waited} -le ${SIGKILL_LIMIT} ]])
        do
            sleep 5
            waited=$(expr ${waited} + 5)
            info "continue killing process:kill -n 9 ${target_pid}, has waited for ${waited} seconds..."
            kill -n 9 ${target_pid}
            is_running
        done
    fi
    
    is_running
    if [[ $? -eq 0 ]];then
        warning "Stop failed: target_pid=${target_pid}"
        return 1
    else
        info "Stop success: target_pid=${target_pid}"
        return 0
    fi
}

# 重启
function restart() {
    stop
    if [[ $? -ne 0 ]];then
        warning "Call stop failed in restart!"
        return 1
    fi
    start
    if [[ $? -ne 0 ]];then
        warning "Call start failed in restart!"
        return 1
    fi
    return 0
}

# 运行状态
# 0 -> running, 1 -> starting, 2 -> stopped
function status() {
    is_running
    ret_val=$?
    if [[ ${ret_val} -eq 0 ]];then
        supervise_pid=$(get_ppid)
        target_pid=$(get_pid)
        info "Supervise process id: ${supervise_pid}, target process id: ${target_pid}"
        return 0
    else
        info "Process or supervise is not running!"
        return 2
    fi
    return 2
}

# 帮助
function help() {
    echo "Usage: bin/noah_contorl start/restart/stop/status"
}

# 检查文件和目录是否存在以类型
function check_existence_and_type() {
    local path=$1
    local type=$2

    if ([[ ${type} == "file" ]] && [[ -f ${path} ]]) || 
        ([[ ${type} == "dir" ]] && [[ -d ${path} ]]);then
        info "${path} is a ${type}"
        return 0
    else
        info "${path} is not a ${type}. Unexpected!"
        return 1
    fi
}

# 检查环境，主要是检查supervise的文件
function check_and_prepare_env() {
    # SUPERVISE_CONF is not necessary, leave it alone
    check_existence_and_type ${SUPERVISE} "file"
    if [[ $? -eq 0 ]];then
        chmod +x ${SUPERVISE}
        if [[ $? -ne 0 ]];then
            warning "chmod +x ${SUPERVISE} failed!"
            exit 1
        fi
    fi

    check_existence_and_type ${STATUS_DIR} "dir"
    if [[ $? -eq 0 ]];then
        mkdir -p ${STATUS_DIR}
        if [[ $? -ne 0 ]];then
            warning "mkdir -p ${STATUS_DIR} failed!!"
            exit 1
        fi
    fi
}

# 批量替换配置文件的后缀
function replace_conf_files_by_suffix() {
    echo $1
    case $1 in
      Online) # 修改为线上配置
        logging "使用 toml.online 替换为 toml"
        rename toml\.online toml conf/*.online
        ;;
      *)
        logging "默认使用 toml 为线下测试环境"
        ;;
    esac
}

########################control###################################

check_and_prepare_env 

ACTION=$1

# 检查端口 ################################################
if [ -z "$MATRIX_INSTANCE_PORTS" ]; then
  logging "未指定端口,使用配置文件中的端口号"
  PORT_IN_CONF=$(grep "http_port" "$BASE_DIR"/conf/web.toml | awk -F '= ' {'print $2'})
  logging "$PORT_IN_CONF"
  MATRIX_INSTANCE_PORTS="main=$PORT_IN_CONF"
fi

logging "运行端口号参数为 $MATRIX_INSTANCE_PORTS"
PORT_MAIN=$(echo $MATRIX_INSTANCE_PORTS | tr , '\n' | awk -F'=' '$1=="main"{print $2}')
########################################################################


# 检查运行环境，替换配置文件 .toml ########################
if [ -z "$ENV" ]; then
  logging "未指定运行环境,不对配置文件进行替换"
else
  case $ENV in
    Online)
        logging "当前为线上环境,使用.online配置文件"
        replace_conf_files_by_suffix "$ENV"
        ;;
    *)
        logging "不支持的运行环境值$ENV,不对配置文件进行替换,默认测试环境配置"
        ;;
  esac
fi
################################################

case "${ACTION}" in
    start)
        logging "Starting ..."
        start
        ret_val=$?
        logging "Started" 
    ;;
    stop)
        logging "Stoping..."
        stop
        ret_val=$?
        logging "Stopped"
    ;;
    restart)
        logging "Restarting..."
        restart
        ret_val=$?
        logging "Restarted"
    ;;
    status)
        logging "Getting status..."
        status
        ret_val=$?
        logging "Finished getting status" 
    ;;
    *)
        help
        exit 1
    ;;
esac

exit ${ret_val}
